package com.sprite.utils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.sprite.utils.conversion.Converts;
import org.w3c.dom.Element;
import org.w3c.dom.Node;


/**
 * @author Jack
 */
public final class UtilBeans {

    /**
     * 实例化
     *
     * @param clazz
     *         泛型类型
     * @param <T>
     *         泛型类型
     * @return 泛型实例
     * @throws InstantiationException
     *         实例化异常
     * @throws IllegalAccessException
     *         参数异常
     */
    public static <T> T instance(Class<T> clazz) throws InstantiationException, IllegalAccessException {
        return clazz.newInstance();
    }

    /**
     * 判断对象是否Bean
     * <p>如下情况不是：</p>
     * <p>1、继承了 Number</p>
     * <p>2、继承了 CharSequence</p>
     * <p>3、基础类型的type，如：Integer.TYPE	， Long.TYPE</p>
     *
     * @param clazz
     *         Class
     * @return 是否为java bean
     * 
     * @deprecated 
     * @see UtilClass#isBean(Class)
     */
    
    @Deprecated
    public static boolean isBean(Class<?> clazz) {
       return UtilClass.isBean(clazz);
    }

    /**
     * 将xml转为对象
     *
     * @param element
     *         xml 节点
     * @param clazz
     *         类
     * @param <T>
     *         泛型类型
     * @return 实例
     * @throws InstantiationException
     *         实例化异常
     * @throws IllegalAccessException
     *         实例化参数异常
     */
    public static <T> T fromXml(Element element, Class<T> clazz) throws InstantiationException, IllegalAccessException {
        T t = instance(clazz);
        List<Element> elements = UtilXml.childElementList(element);
        for (Element el : elements) {
            String propertyName = adjustBeanPropertyName(el.getTagName());
            if (UtilXml.hasChildElement(el)) {
                Object value = fromXml(element, UtilBeans.getPropertyType(clazz, propertyName));
                setProperty(t, propertyName, value);
            } else {
                Node node = el.getFirstChild();
                setProperty(t, propertyName, node.getTextContent());
            }
        }

        return t;
    }

    /**
     * 将xml转为对象
     *
     * @param xmlString
     *         xml 字符
     * @param clazz
     *         目标类
     * @param <T>
     *         泛型
     * @return 目标类的实例
     * @throws Exception
     *         异常
     */
    public static <T> T fromXml(String xmlString, Class<T> clazz) throws Exception {
        return fromXml(UtilXml.readXmlDocument(xmlString).getDocumentElement(), clazz);
    }

    /**
     * 修正属性名称，符合Java属性命名规范
     * <p>例："stu_name" -> "stuName"</p>
     * <p>例："s_name" -> "SName"</p>
     * <p>例："s_t_name" -> "STName"</p>
     *
     * @param propertyName
     * @return 属性名称
     */
    private static String adjustBeanPropertyName(String propertyName) {
        if (UtilString.isBlank(propertyName)) {
            return null;
        }

        StringBuilder builder = new StringBuilder();

        boolean underline = false;
        char[] chars = propertyName.toCharArray();

        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '_') {
                underline = true;
                continue;
            }

            if (underline) {// 如果字符前面是下划线，转大写
                builder.append(Character.toUpperCase(chars[i]));
                underline = false;
            } else {
                builder.append(chars[i]);
                underline = false;
            }
        }

        if (builder.length() == 1) {
            return builder.toString().toUpperCase();
        }

        if (Character.isUpperCase(builder.charAt(1))) {// 如果第二个字母大写，则将首字母大写
            builder.setCharAt(0, Character.toUpperCase(builder.charAt(0)));
        } else {
            builder.setCharAt(0, Character.toLowerCase(builder.charAt(0)));
        }

        return builder.toString();

    }

    /**
     * 设值
     *
     * @param obj
     *         目标对象
     * @param propertyName
     *         目标属性名称
     * @param value
     *         属性值
     */
    public static void setProperty(Object obj, String propertyName, Object value) {
        try {
            PropertyDesc desc = new PropertyDesc(propertyName, obj.getClass());
            if (desc.getFront() == null) {
                desc.setValue(obj, value);
                return;
            }
            Method wm = desc.getFront().getWriteMethod();
            if (desc.getBack() == null) {
                if (value == null) {
                    wm.invoke(obj, value);
                } else {
                    Class<?>[] clazzs = wm.getParameterTypes();
                    if (clazzs != null && clazzs.length == 1) {
                        Class<?> targetClass = clazzs[0];
                        if (!targetClass.equals(value.getClass()) && !targetClass.isAssignableFrom(value.getClass())) {
                            value = Converts.convert(value, targetClass);
                        }
                    }
                    desc.setValue(obj, value);
                }

            } else {
                Method rmMethod = desc.getFront().getReadMethod();

                Object target = rmMethod.invoke(obj);
                if (target == null) {
                    target = desc.getFront().getPropertyType().newInstance();
                }

                setProperty(target, desc.getBack(), value);
                desc.setValue(obj, target);
            }
        } catch (Exception e) {
            throw new RuntimeException(
                    "Could not set value of property '" + propertyName + "' from source to target [" + obj.getClass() + "]", e);
        }
    }

    /**
     * 获取值
     *
     * @param obj
     *         目标对象
     * @param propertyName
     *         目标属性名称
     * @return 属性值
     */
    public static Object getProperty(Object obj, String propertyName) {
        try {
            PropertyDesc desc = new PropertyDesc(propertyName, obj.getClass());
            if (desc.getBack() == null) {
                return desc.getValue(obj);
            } else {
                Method rmMethod = desc.getFront().getReadMethod();

                Object target = rmMethod.invoke(obj);
                if (target == null) {
                    target = desc.getFront().getPropertyType().newInstance();
                }

                return getProperty(target, desc.getBack());
            }
        } catch (Exception e) {
            throw new RuntimeException(
                    "Could not get value of property '" + propertyName + "' from source to target", e);
        }
    }

    /**
     * 获取属性的类型
     *
     * @param clazz
     *         目标类型
     * @param propertyName
     *         属性名称
     * @return 属性的类型
     */
    public static Class<?> getPropertyType(Class<?> clazz, String propertyName) {
        try {
            PropertyDesc desc = new PropertyDesc(propertyName, clazz);
            if (desc.getBack() == null) {
                return desc.getType();
            } else {
                return getPropertyType(desc.getType(), desc.getBack());
            }
        } catch (Exception e) {
            throw new RuntimeException(
                    "Could not get value of property '" + propertyName + "' from source to target", e);
        }
    }

    /**
     * 设置JavaBean属性
     *
     * @param obj
     *         目标对象
     * @param props
     *         属性值
     */
    public static void setProperties(Object obj, Map<String, Object> props) {
        for (Map.Entry<String, Object> entry : props.entrySet()) {
            setProperty(obj, entry.getKey(), entry.getValue());
        }
    }

    /**
     * 复制属性值到目标对象
     *
     * @param source
     *         源对象
     * @param sourceProps
     *         源对象的属性名称
     * @param target
     *         目标对象
     * @param targetProps
     *         目标对象的属性名称
     */
    public static void copyProperties(Object source, String[] sourceProps, Object target, String[] targetProps) {
        if (sourceProps == null || sourceProps.length == 0 || targetProps == null || targetProps.length == 0) {
            throw new IllegalArgumentException("sourceProps targetProps is null");
        }

        if (sourceProps.length != targetProps.length) {
            throw new IllegalArgumentException("length is not same");
        }

        for (int i = 0; i < sourceProps.length; i++) {
            String sourceProp = sourceProps[i];
            String targetProp = targetProps[i];

            Object value = getProperty(source, sourceProp);

            if (value == null) {
                continue;
            }

            setProperty(target, targetProp, value);
        }

    }


    public interface FilterCallback {
        Object filter(String field, Object value);
    }

    /**
     * 将对象转为map
     *
     * @param source
     *         源对象
     * @param sourceProps
     *         源对象的属性名称
     * @param alias
     *         别名
     * @param filterCallback
     *         过滤函数
     * @return map
     */
    public static Map<String, Object> toMapWithFilter(Object source, String[] sourceProps, String[] alias, FilterCallback filterCallback) {
        Map<String, Object> result = new HashMap<String, Object>();

        if (sourceProps == null || sourceProps.length == 0) {
            throw new IllegalArgumentException("sourceProps  is null");
        }


        for (int i = 0; i < sourceProps.length; i++) {
            String sourceProp = sourceProps[i];
            Object value = null;

            int index = sourceProp.indexOf("[]");
            if (index > 0) {
                // 处理集合

            } else {
                value = getProperty(source, sourceProp);
            }

            if (filterCallback != null) {
                value = filterCallback.filter(sourceProp, value);
            }

            if (value == null) {
                continue;
            }

            String prop = sourceProp;
            if (alias != null && alias.length > i) {
                prop = alias[i];
            }

            result.put(prop, value);
        }

        return result;

    }

    /**
     * 将对象的指定属性值转为Map，忽略值为null的属性，
     *
     * @param source
     *         源对象
     * @param sourceProps
     *         需要转换的属性
     * @param alias
     *         属性别名
     * @return map
     */
    public static Map<String, Object> toMap(Object source, String[] sourceProps, String[] alias) {
        return toMapWithFilter(source, sourceProps, alias, null);
    }

    /**
     * 将一个Bean 转为map
     *
     * @param source
     *         源对象
     * @return map
     */
    public static Map<String, Object> toMap(Object source) {
        return toMap(source, true);
    }

    /**
     * @param source
     *         源对象
     * @param ignoreNull
     *         是否忽略值为null的属性
     * @return map
     */
    public static Map<String, Object> toMap(Object source, boolean ignoreNull) {
        if (Map.class.isInstance(source)) {
            return UtilMisc.cast(source);
        }

        Map<String, Object> map = new HashMap<>();

        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(source.getClass(), Introspector.IGNORE_ALL_BEANINFO);
            String field = null;
            Object value = null;
            for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
                if ("class".equals(descriptor.getName())) {
                    continue;
                }
                field = descriptor.getName();
                value = descriptor.getReadMethod().invoke(source);
                if (!ignoreNull) {
                    map.put(field, value);
                } else if (value != null) {
                    map.put(field, value);
                }

            }
            return map;
        } catch (Exception e) {
            throw new RuntimeException(
                    "convert object to map error", e);
        }

    }

    /**
     * 将对象的指定属性值转为Map，忽略值为null的属性
     *
     * @param source
     *         源对象
     * @param sourceProps
     *         源对象的属性名称
     * @return map
     */
    public static Map<String, Object> toMap(Object source, String[] sourceProps) {
        return toMap(source, sourceProps, null);
    }


    /**
     * 将集合转为map列表
     *
     * @param source
     *         源对象
     * @param sourceProps
     *         源对象的属性名称
     * @param alias
     *         别名
     * @param other
     *         额外参数，追加到每个列表项中
     * @param filterCallback
     *         过滤函数
     * @return map列表
     */
    public static List<Map<String, Object>> toMap(Collection<?> source, String[] sourceProps, String[] alias, Map<String, Object> other, FilterCallback filterCallback) {
        List<Map<String, Object>> list = new LinkedList<>();
        for (Object object : source) {
            Map<String, Object> map = toMapWithFilter(object, sourceProps, alias, filterCallback);
            if (other != null) {
                map.putAll(other);
            }
            list.add(map);
        }
        return list;
    }

    /**
     * @param source
     *         源对象
     * @param sourceProps
     *         源对象属性
     * @param alias
     *         属性别名
     * @param other
     *         额外参数，追加到每个列表项中
     * @return map 集合
     */
    public static List<Map<String, Object>> toMap(Collection<?> source, String[] sourceProps, String[] alias, Map<String, Object> other) {
        return toMap(source, sourceProps, alias, null, null);
    }

    /**
     * @param source
     *         源对象
     * @param sourceProps
     *         源对象属性
     * @param alias
     *         属性别名
     * @return map 集合
     */
    public static List<Map<String, Object>> toMap(Collection<?> source, String[] sourceProps, String[] alias) {
        return toMap(source, sourceProps, alias, null);
    }

    public static List<Map<String, Object>> toMap(Collection<?> source, String[] sourceProps) {
        List<Map<String, Object>> list = new LinkedList<>();
        for (Object object : source) {
            list.add(toMap(object, sourceProps, null));
        }
        return list;
    }


    /**
     * 获取集合中对应的某个属性值
     *
     * @param source
     *         源对象
     * @param prop
     *         属性名称
     * @return 属性值集合
     */
    public static List<Object> toList(Collection<?> source, String prop) {
        List<Object> list = new LinkedList<Object>();
        for (Object object : source) {
            Object value = getProperty(object, prop);
            list.add(value);
        }
        return list;
    }

    private static class PropertyDesc {
        public static final String SEPARATOR = ".";
        private PropertyDescriptor front;
        private String property;
        private String back;

        public PropertyDesc(String property, Class<?> clazz) throws IntrospectionException {
            int position = property.indexOf(SEPARATOR);

            if (position < 0) {
                this.property = property;
            } else {
                this.property = property.substring(0, position);
                back = property.substring(position + 1);
            }

            if (!Map.class.isAssignableFrom(clazz)) {
                front = new PropertyDescriptor(this.property, clazz);
            }
        }

        public PropertyDescriptor getFront() {
            return front;
        }

        public String getBack() {
            return back;
        }

        public Object getValue(Object obj) throws ReflectiveOperationException {
            if (Map.class.isInstance(obj)) {
                return ((Map<?, ?>) obj).get(this.property);
            }

            if (front != null) {
                return front.getReadMethod().invoke(obj);
            }
            return null;
        }

        @SuppressWarnings({"unchecked", "rawtypes"})
        public void setValue(Object obj, Object value) throws ReflectiveOperationException {
            if (Map.class.isInstance(obj)) {
                ((Map) obj).put((Object) this.property, value);
            }

            if (front != null) {
                front.getWriteMethod().invoke(obj, value);
            }
        }

        public Class<?> getType() {
            return front.getPropertyType();
        }
    }

    private UtilBeans() {
    }
}
